-
Notifications
You must be signed in to change notification settings - Fork 280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a --color flag to the CLI and disable colors for Pkl test #571
Conversation
The --colors flag makes it possible to forcefully disable colour output, and also forcefully enable it. We make use of this in the native Pkl test engines by passing --colors=never into the pkl commands, which ensures we get a predictable output. Additionally the pkl test command also sets --colors=never to ensure that pkl test output isn't accidentially contaminated with ansi escape chars, which will break example based tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor comments!
Also, this is missing documentation (cli-common-options.adoc, gradle-common-properties.adoc). Do you mind adding them?
@@ -134,6 +134,7 @@ data class CliBaseOptions( | |||
|
|||
/** Hostnames, IP addresses, or CIDR blocks to not proxy. */ | |||
val httpNoProxy: List<String>? = null, | |||
val colors: String = "auto" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make this an enum:
enum class Color {
/**
* Output ANSI-formatted text only when the process STDOUT/STDERR are attached to a TTY.
*/
AUTO,
/**
* Never output ANSI-formatted text.
*/
NEVER,
/**
* Always output ANSI-formatted text.
*/
ALWAYS
}
And change this type to said enum, and also add a doc comment. Also, nit: s/colors/color
val colors: String = "auto" | |
/** Whether to output ANSI-formatted text. */ | |
val color: Color = Color.AUTO |
val colors: String by | ||
option(names = arrayOf("--colors"), help = "Enable or disable colour output in the terminal") | ||
.choice("auto", "never", "always") | ||
.default("auto") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's follow the convention set by unix-y tools like ls
, diff
, grep
, etc, and call this --color
.
Also, with the above suggestion to enum-ify this (requires import com.github.ajalt.clikt.parameters.types.enum
)
val colors: String by | |
option(names = arrayOf("--colors"), help = "Enable or disable colour output in the terminal") | |
.choice("auto", "never", "always") | |
.default("auto") | |
val color: Color by | |
option(names = arrayOf("--color"), help = "Enable or disable colour output in the terminal") | |
.enum<Color>(key = { it.name.lowercase() }) | |
.default(Color.AUTO) |
@@ -230,7 +237,8 @@ class BaseOptions : OptionGroup() { | |||
noProject = projectOptions?.noProject ?: false, | |||
caCertificates = caCertificates, | |||
httpProxy = proxy, | |||
httpNoProxy = noProxy ?: emptyList() | |||
httpNoProxy = noProxy ?: emptyList(), | |||
colors = if (disableColors) "never" else colors, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
colors = if (disableColors) "never" else colors, | |
color = color |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This exists to provide a mechanism for the test
subcommand to forcefully disable not just colours, but also the injection if ANSI codes completely. I've been having issues because the pkl test
expected output because they don't travel along the standard output rails. As a consequence the ANSI codes don't get stripped if they're injected.
By providing this escape hatch, I can ensure that colour are disabled before there's any opportunity for the Jansi formatting object to init, because once it's created you can't disable the injection of ANSI codes, only strip them out later. That mixed with the fact that caught errors in pkl test
are truncated creates a bit of nightmare as the outputs get truncated to slightly different lengths depending on if colours were enabled, regardless of if those ANSI code actually made it to the output.
I tried to figure out where the error truncation etc was happening, so I could modify it to handle ANSI/non-ANSI outputs in an equivalent way, but I haven't been able to track down where it happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if (when?) we switch to a text formatter, we can control this ourselves by passing a no-op text formatter when --color=never
. Then, we're in full control of how formatting happens rather than mutating global state.
Anyways, I don't know if that's relevant to my suggestion here. CliBaseOptions
is a data class, and you can always modify it however you want because you get a derived copy
method.
val options = baseOptions(myModules).copy(color = Color.NEVER)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only relevant due to how Jansi handles global state. It's caught me out a couple of times because Jansi doesn't handle changes to global state the way you might expect. The final result is what you expect, but the mechanism by which Jansi achieves that result depends on the exact order of operations (mutating global state before or after doing the initial Jansi setup). In most cases it doesn't make any practical difference, but for the specific case of Pkl test it does (which I learned the hard way).
For this specific case I need to make sure the baseOptions
are set correctly before the common CLI setup function runs, because that's where Jansi setup happens. It's not enough to only modify the state within the execution context of the test command (which is what I originally tried).
Agree we can just get rid of this hack by implementing a proper formatting API, and having direct control over how control codes are injected. It's also a good argument to do a proper formatting API sooner rather than later. The number of hacks and shortcuts I've had to introduce to get Jansi working as expected is getting pretty uncomfortable at this point. So I'll probably look towards doing that, rather than continuing further with this approach.
@@ -208,7 +214,8 @@ class BaseOptions : OptionGroup() { | |||
fun baseOptions( | |||
modules: List<URI>, | |||
projectOptions: ProjectOptions? = null, | |||
testMode: Boolean = false | |||
testMode: Boolean = false, | |||
disableColors: Boolean = false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
disableColors: Boolean = false, |
@@ -166,7 +166,8 @@ protected CliBaseOptions getCliBaseOptions() { | |||
getTestPort().getOrElse(-1), | |||
Collections.emptyList(), | |||
getHttpProxy().getOrNull(), | |||
getHttpNoProxy().getOrElse(List.of())); | |||
getHttpNoProxy().getOrElse(List.of()), | |||
"auto"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this means that Gradle will not output any colors, because this is executed as a child process.
Anyway, we should provide the same options in Gradle (auto, never, always). Take a look at how the other options are configured to see what to do here. Or, leave a TODO comment here so we can follow up in a future PR.
This may be more complicated than what it's worth, but really, I don't think this is something that should hold you back, especially considering we want to try and avoid the Jansi dependency in |
Closing this; superseded by #746 |
The --colors flag makes it possible to forcefully disable colour output, and also forcefully enable it. We make use of this in the native Pkl test engines by passing --colors=never into the pkl commands, which ensures we get a predictable output.
Additionally the pkl test command also sets --colors=never to ensure that pkl test output isn't accidentially contaminated with ansi escape chars, which will break example based tests.